package cn.ztuo.bitrade.service;


import cn.ztuo.bitrade.constant.BooleanEnum;
import cn.ztuo.bitrade.constant.TransactionType;
import cn.ztuo.bitrade.core.DB;
import cn.ztuo.bitrade.core.DataException;
import cn.ztuo.bitrade.dao.ContractCoinMapper;
import cn.ztuo.bitrade.dao.ContractOrderMapper;
import cn.ztuo.bitrade.entity.*;
import cn.ztuo.bitrade.pagination.Criteria;
import cn.ztuo.bitrade.pagination.Restrictions;
import cn.ztuo.bitrade.service.Base.BaseService;
import cn.ztuo.bitrade.util.*;
import com.alibaba.fastjson.JSON;
import com.querydsl.core.types.Predicate;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.*;

@SuppressWarnings("AlibabaTransactionMustHaveRollback")
@Slf4j
@Service
public class ContractOrderService extends BaseService {

    @Autowired
    private ContractOrderMapper orderMapper;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ContractWalletService contractWalletService;
    @Autowired
    private LocaleMessageSourceService msService;
    @Autowired
    private MemberService memberService;
    @Autowired
    private PlatformTransactionService platformTransactionService;
    @Autowired
    private MemberGradeService gradeService;
    @Autowired
    private ContractOrderDetailService ContractOrderDetailService;
    @Autowired
    private ContractCoinMapper contractCoinMapper;
    @Autowired
    private ContractPositionService positionService;
    @Autowired
    private ContractPositionForceService positionForceService;
    @Autowired
    private ContractOrderDetailService contractOrderDetailService;
    @Autowired
    private ContractOrderDetailAggregationService contractOrderDetailAggregationService;
    @Autowired
    private MemberTransactionService transactionService;
    @Autowired
    private ContractPlatformService platformService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private RobotTransactionService robotTransactionService;

    private Thread robotOrderThread;

    public List<ContractOrder> findByPositionFlagNotAndStatusInAndMemberId(ContractEnumType.ContractOrderPositionFlag flag, List<Integer> status, Long memberId) {
        return orderMapper.findByPositionFlagNotAndStatusInAndMemberId(flag, status, memberId);
    }

    public List<ContractOrder> findAllByMemberId(Long memberId, String symbol) {
        return orderMapper.findAllByMemberIdAndSymbol(memberId, symbol);
    }

    public List<ContractOrder> findAllByMemberId(Long memberId) {
        return orderMapper.findAllByMemberId(memberId);
    }

    @Transactional
    public ContractOrder saveAndFlush(ContractOrder order) {
        order = orderMapper.saveAndFlush(order);
        return order;
    }

    /**
     * 添加委托订单
     *
     * @param memberId
     * @param order
     * @return
     */
    @Transactional
    public MessageResult addOrder(Long memberId, ContractOrder order) {
        ContractWallet wallet = contractWalletService.findByMemberId(memberId);
        if (wallet.getIsLock().equals(BooleanEnum.IS_TRUE)) {
            return MessageResult.error("钱包已锁定");
        }
        if (wallet == null) {
            return MessageResult.error("合约余额不足");
        }
        ContractPosition position = positionService.getByPositionTypeAndSymbolAndMemberId(order.getPositionType(), order.getSymbol(), order.getMemberId());
        if (position != null && position.getIsLock().equals(BooleanEnum.IS_TRUE)) {
            return MessageResult.error("仓位已锁定");
        }
        order.setListTime(Calendar.getInstance().getTimeInMillis());
        ContractOrderStatus subStatus = order.getStatus();
        if (order.getType() == ContractOrderType.CHECK_FULL_STOP) {
            order.setStatus(ContractOrderStatus.WAITING_TRIGGER);
            order.setPositionFlag(ContractEnumType.ContractOrderPositionFlag.throwout);
        } else if (order.getType() == ContractOrderType.PLAN_PRICE) {
            order.setStatus(ContractOrderStatus.WAITING_TRIGGER);
            order.setPositionFlag(ContractEnumType.ContractOrderPositionFlag.designate);
        } else {
            order.setStatus(ContractOrderStatus.TRADING);
        }
        order.setTradedAmount(BigDecimal.ZERO);
        order.setOrderId(GeneratorUtil.getOrderId("C"));
        log.info("add order:{}", order);

        //如果挂单是开仓 冻结起始保证金资金-----平仓不需要再次冻结保证金
        if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
            MessageResult result = contractWalletService.addfrozenBalance(wallet, order.getStart());
            if (result.getCode() != 0) {
                return MessageResult.error(500, msService.getMessage("INSUFFICIENT_COIN") + order.getBaseSymbol());
            }
        }
        if ((position == null || position.getContractOccupyNum() == 0) && order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.throwout) {
            return MessageResult.error("持仓可用合约数量不足!");
        }
        if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.throwout) {
            if (order.getPositionOprationType() == ContractEnumType.ContractOrderPositionOprationType.force) {
                ContractPositionForce positionForce = positionForceService.saveAndFlush(JSON.parseObject(JSON.toJSONString(position), ContractPositionForce.class));
                position.setPredictForcePrice(null);
                position.setContractTotalNum(0);
                position.setContractOccupyNum(0);
                position.setPositionAvgPrice(null);
                position.setHoldingBond(BigDecimal.ZERO);
                position.setPositionTotalValue(BigDecimal.ZERO);
                position.setUseableBond(BigDecimal.ZERO);
                positionService.delPosition(position);
            } else {
                int oldNum = position.getContractOccupyNum();
                position.setContractOccupyNum(oldNum - order.getNowp().intValue());
                position.setUseableBond(position.getUseableBond().subtract(order.getStart()));
                if (position.getContractOccupyNum() != (oldNum - order.getNowp().intValue())) {
                    return MessageResult.error(500, msService.getMessage("INSUFFICIENT_COIN") + order.getBaseSymbol());
                }
                positionService.saveAndFlush(position);
            }
        }
        order = orderMapper.saveAndFlush(order);
        //修改缓存 个状态下 对应的 数量总和
        if (order != null && ContractUtils.updateContractMaxNowpMap(memberId, order.getSymbol(), subStatus, order.getStatus(), order.getNowp())) {
            //持有仓位更改杠杆
            //更新缓存中 各交易状态 对应累计的 交易合约张数
            return MessageResult.success("success");
        } else {
            return MessageResult.error(500, "error");
        }
    }

    public List<ContractOrder> findByDirection(String symbol) {
        return orderMapper.findBySymbol(symbol);
    }

    public List<ContractOrder> findAllTradingOrderBySymbol(String symbol) {
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        specification.add(Restrictions.eq("symbol", symbol, false));
        specification.add(Restrictions.eq("status", ContractOrderStatus.TRADING, false));
        return orderMapper.findAll(specification);
    }

    @Transactional(rollbackFor = Exception.class)
    public MessageResult orderCanceled(String orderId, BigDecimal start, BigDecimal tradedAmount, BigDecimal turnover) throws SQLException, DataException {
        ContractOrder order = findOne(orderId);
        if (order == null) {
            return MessageResult.error("order not exists");
        }
        if (order.getStatus() != ContractOrderStatus.TRADING && order.getStatus() != ContractOrderStatus.WAITING_TRIGGER) {
            return MessageResult.error(500, "order not in trading");
        }
        //order.setTradedAmount(tradedAmount);
        //order.setTurnover(turnover);
        order.setStatus(ContractOrderStatus.CANCELED);
        order.setCanceledTime(Calendar.getInstance().getTimeInMillis());
        //未成交的退款
        //根据memberId锁表，防止死锁
        DB.query("select id from contract_wallet where member_id = ? for update;", order.getMemberId());
        DB.query("select id from contract_position where member_id = ? and contract_position_type = ? for update;", order.getMemberId() , order.getPositionType());
        boolean success = orderRefund(order, start, tradedAmount, turnover, ContractEnumType.CanceledOrCompleted.canceled);
        if(success){
            return MessageResult.success("orderCanceled success");
        }else{
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return MessageResult.error("orderCanceled fall");
        }
    }

    /**
     * 委托退款，如果取消订单或成交完成有剩余
     *
     * @param order
     * @param tradedAmount
     * @param turnover
     */
    public boolean orderRefund(ContractOrder order, BigDecimal start, BigDecimal tradedAmount, BigDecimal turnover, ContractEnumType.CanceledOrCompleted type) {
        //下单时候冻结的币，实际成交应扣的币
        //撤销 或 完成 时候 保证金 按照 数量比率进行计算
        //100手 成交 30手  则成交部分的保证金= 开仓保证金*(30/100)  剩余的保证金 = 开仓保证金- 成交部分保证金
        //下单币种数量(coninSymbol)，实际成交币数量
        BigDecimal frozenAmount, dealAmount;
        frozenAmount = order.getAmount();
        dealAmount = tradedAmount;
        boolean success = false;

        BigDecimal surpluAmount = frozenAmount.subtract(dealAmount);
        ContractCoin coin = ContractUtils.getBaseSymbol(order.getSymbol());
        BigDecimal subNowp = surpluAmount.divide(coin.getMinVolume());

        ContractPosition position = positionService.getByPositionTypeAndSymbolAndMemberId(order.getPositionType(), order.getSymbol(), order.getMemberId());
        ContractWallet contractWallet = contractWalletService.findByMemberId(order.getMemberId());

        if (start.compareTo(BigDecimal.ZERO) > 0) {
            //开仓委托单取消,直接退回 余额钱包;
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
                MessageResult mr = contractWalletService.thawBalance(contractWallet, start);
                if (mr.getCode() == 0) {
                    success = true;
                }else{
                    success = false;
                    log.error("退回冻结资金失败,order={},tradedAmount={},turnover={}", order, tradedAmount, turnover);
                }
            }
            //平仓委托单取消,直接退回 持有仓位;
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.throwout) {
                MessageResult mr = positionService.thawBalance(position, subNowp.intValue(),start);
                if (mr.getCode() == 0) {
                    success = true;
                }else{
                    success = false;
                    log.error("退回冻结资金失败,order={},tradedAmount={},turnover={}", order, tradedAmount, turnover);
                }
            }
        }
        return success;
    }

    public ContractOrder findOne(String orderId) {
        return orderMapper.findOne(orderId);
    }

    /**
     * 处理交易匹配
     *
     * @param trade
     * @return
     * @throws Exception
     */
    @Transactional
    public MessageResult processExchangeTrade(ContractTrade trade) throws Exception {
        log.info("processExchangeTrade,trade = {}", trade);
        if (trade == null || trade.getBuyOrderId() == null || trade.getSellOrderId() == null) {
            return MessageResult.error(500, "trade is null");
        }
        ContractOrder buyOrder = orderMapper.findByOrderId(trade.getBuyOrderId());
        ContractOrder sellOrder = orderMapper.findByOrderId(trade.getSellOrderId());
        if (buyOrder == null || sellOrder == null) {
            log.error("order not found");
            return MessageResult.error(500, "order not found");
        }

        Member buyMember = memberService.findOne(buyOrder.getMemberId());
        Member sellMember = memberService.findOne(sellOrder.getMemberId());


        //处理买入订单
        processOrder(buyOrder, trade, buyMember);
        //处理卖出订单
        processOrder(sellOrder, trade, sellMember);


        return MessageResult.success("process success");
    }

    /**
     * 对发生交易的委托处理相应的钱包
     *
     * @param order  委托订单
     * @param trade  交易详情
     * @param member 会员
     *               * @return
     */
    public void processOrder(ContractOrder order, ContractTrade trade, Member member) {
        Long time = Calendar.getInstance().getTimeInMillis();
        //添加成交详情
        //需要释放的保证金
        BigDecimal bond = BigDecimal.ZERO, tradeAmount;
        BigDecimal turnover = BigDecimal.ZERO;
        BigDecimal fee = BigDecimal.ZERO;
        ContractOrder otherOrder = null;

        ContractCoin coin = contractCoinMapper.findBySymbol(order.getSymbol());
        //未实现盈亏;
        BigDecimal benefit = BigDecimal.ZERO;
        //查询用户付出的钱包信息
        ContractWallet wallet = contractWalletService.findByMemberId(order.getMemberId());

        if (order.getDirection() == ContractOrderDirection.BUY) {
            turnover = trade.getBuyTurnover();
            bond = trade.getBuyBond();
            otherOrder = orderMapper.findByOrderId(trade.getSellOrderId());

        }
        if (order.getDirection() == ContractOrderDirection.SELL) {
            turnover = trade.getSellTurnover();
            bond = trade.getSellBond();
            otherOrder = orderMapper.findByOrderId(trade.getBuyOrderId());
        }
        ContractPosition position = positionService.getByPositionTypeAndSymbolAndMemberId(order.getPositionType(), order.getSymbol(), order.getMemberId());
        ContractPosition otherPosition = positionService.getByPositionTypeAndSymbolAndMemberId(otherOrder.getPositionType(), otherOrder.getSymbol(), otherOrder.getMemberId());
        if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.throwout) {
            benefit = trade.getAmount().multiply(trade.getPrice().subtract(position.getPositionAvgPrice()));
            fee = turnover.multiply(coin.getProvideFee());
        }
        if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
            fee = turnover.multiply(coin.getMentionFee()).negate();
        }
        ContractOrderDetail orderDetail = new ContractOrderDetail();
        orderDetail.setOrderId(order.getOrderId());
        orderDetail.setPrice(trade.getPrice());
        orderDetail.setBond(bond);
        orderDetail.setAmount(trade.getAmount());
        orderDetail.setTime(time);
        orderDetail.setTurnover(turnover);
        orderDetail.setTradeNowp(trade.getNowp());
        orderDetail.setFee(fee);
        orderDetail.setYingKui(benefit);

        //把成交的明细保存DB
        contractOrderDetailService.save(orderDetail);
        tradeAmount = bond.add(benefit).subtract(fee);
        /**
         * 聚合币币交易订单手续费明细存入mongodb
         */
        ContractOrderDetailAggregation aggregation = new ContractOrderDetailAggregation();
        aggregation.setFee(fee.doubleValue());
        aggregation.setTime(orderDetail.getTime() / 1000);
        aggregation.setDirection(order.getDirection());
        aggregation.setOrderId(order.getOrderId());
        aggregation.setAmount(tradeAmount.doubleValue());

        if (member != null) {
            aggregation.setMemberId(member.getId());
            aggregation.setUsername(member.getUsername());
            aggregation.setRealName(member.getRealName());
        }
        contractOrderDetailAggregationService.save(aggregation);
        Boolean success = false;
        ContractPlatform platform = platformService.get();
        //如果是平仓:释放所占比率保证金到进入余额  释放持有仓位中的相应数据;
        if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.throwout) {
            position.setDealGainLoss(position.getDealGainLoss().add(benefit));
            if (order.getPositionOprationType() == ContractEnumType.ContractOrderPositionOprationType.force) {
                // 从持有仓位中扣除 释放持有仓位中的相应数据;
                ContractPositionForce positionForce = positionForceService.getByPositionTypeAndSymbolAndMemberId(order.getPositionType(), order.getSymbol(), order.getMemberId());
                ContractPositionForce otherPositionForce = positionForceService.getByPositionTypeAndSymbolAndMemberId(otherOrder.getPositionType(), otherOrder.getSymbol(), otherOrder.getMemberId());
                if (otherPositionForce != null && positionForce != null && otherPosition.getId().longValue() != positionForce.getId().longValue()) {
                    MessageResult result = positionForceService.updatePositionByThrowout(positionForce, trade.getAmount().multiply(trade.getPrice()), trade.getAmount().divide(coin.getMinVolume()).intValue(), tradeAmount);
                    // 释放的保证金 + 收益 直接进入保险金
                    platform.setInsurance(platform.getInsurance().add(tradeAmount));
                    if (result != null && result.getCode() == 0) {
                        ContractUtils.updateContractMaxNowpMap(member.getId(), order.getSymbol(), order.getStatus(), ContractOrderStatus.COMPLETED, trade.getAmount().divide(coin.getMinVolume()));
                    }
                }
            } else {
                // 从持有仓位中扣除 释放持有仓位中的相应数据;
                BigDecimal totalValue = position.getPositionTotalValue().subtract(trade.getAmount().multiply(trade.getPrice()));
                BigDecimal totalNum = new BigDecimal(position.getContractTotalNum()).subtract(trade.getNowp());
                BigDecimal avgPrice = totalValue.divide(totalNum.multiply(coin.getMinVolume()), 8, BigDecimal.ROUND_DOWN);

                if (position.getId().longValue() != otherPosition.getId().longValue()) {
                    position.setPositionTotalValue(totalValue);
                    position.setContractTotalNum(totalNum.intValue());
                    position.setPositionAvgPrice(avgPrice);
                    position.setHoldingBond(position.getHoldingBond().subtract(tradeAmount));

                    BigDecimal forcePrice = DataUtil.getForcePrice(position);
                    position.setPredictForcePrice(forcePrice);
//                    positionService.updatePositionByThrowout(position, trade.getAmount().multiply(trade.getPrice()), trade.getNowp().intValue(), bond, avgPrice);
                }
                // 释放的保证金 + 收益 直接进入余额
                contractWalletService.thawBalance(wallet, tradeAmount);
                ContractUtils.updateContractMaxNowpMap(member.getId(), order.getSymbol(), order.getStatus(), ContractOrderStatus.COMPLETED, trade.getAmount().divide(coin.getMinVolume()));
                positionService.saveAndFlush(position);
            }
        }
        // 如果是开仓:相应信息进入持有仓位信息中
        if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.designate) {
            if (position == null) {
                position = new ContractPosition();
                position.setMemberId(order.getMemberId());
                position.setDealGainLoss(BigDecimal.ZERO);
                position.setCurrentMultiple(order.getMultiple().toString());
                position.setSymbol(order.getSymbol());
                position.setPositionType(order.getPositionType());
                position.setPositionTotalValue(turnover);
                position.setContractTotalNum(trade.getNowp().intValue());
                position.setContractOccupyNum(trade.getNowp().intValue());
                position.setPositionAvgPrice(trade.getPrice());
                position.setHoldingBond(bond);
                position.setUseableBond(bond);
                BigDecimal maintain = new BigDecimal(order.getMaintainRate().toString().replaceAll("%", "")).divide(new BigDecimal(100));
                BigDecimal forcePrice = DataUtil.getForcePrice(position);
                position.setPredictForcePrice(forcePrice);
                position.setMaintain(maintain);
            } else {
                BigDecimal totalValue = position.getPositionTotalValue().add(trade.getAmount().multiply(trade.getPrice()));
                BigDecimal totalNum = new BigDecimal(position.getContractTotalNum()).add(trade.getNowp());
                BigDecimal avgPrice = totalValue.divide(totalNum.multiply(coin.getMinVolume()), 8, BigDecimal.ROUND_DOWN);

                if (!order.getMemberId().equals(otherOrder.getMemberId()) || order.getPositionType() != otherOrder.getPositionType()) {
                    position.setPositionTotalValue(totalValue);
                    position.setUseableBond(position.getUseableBond().add(bond));
                    position.setContractTotalNum(position.getContractTotalNum() + trade.getNowp().intValue());
                    position.setPositionAvgPrice(avgPrice);
                    position.setContractOccupyNum(position.getContractOccupyNum() + trade.getNowp().intValue());
                    position.setHoldingBond(position.getHoldingBond().add(tradeAmount));

                    BigDecimal forcePrice = DataUtil.getForcePrice(position);
                    position.setPredictForcePrice(forcePrice);


//                    result = positionService.updatePositionByDesignate(position,
//                            trade.getAmount().multiply(trade.getPrice()),
//                            trade.getNowp().intValue(),
//                            avgPrice,
//                            forcePrice,
//                            order.getMultiple().toString(),
//                            tradeAmount);
                } else {
                    position.setContractOccupyNum(position.getContractOccupyNum() + trade.getNowp().intValue());
                    position.setUseableBond(position.getUseableBond().add(bond));
                }
            }
            positionService.saveAndFlush(position);
            ContractUtils.updateContractMaxNowpMap(member.getId(), order.getSymbol(), order.getStatus(), ContractOrderStatus.COMPLETED, trade.getAmount().divide(coin.getMinVolume()));
        }
        platform.setAmount(platform.getAmount().add(fee));
        platformService.saveAndFlush(platform);

        if (order.getOrderResource() != ContractOrderResource.ROBOT) {
            //普通币币交易,增加入资金记录
            MemberTransaction transaction = new MemberTransaction();
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.throwout) {
                transaction.setAmount(tradeAmount);
            } else {
                transaction.setAmount(tradeAmount.negate());
            }
            transaction.setSymbol(trade.getSymbol());
            transaction.setAddress("");
            transaction.setMemberId(order.getMemberId());
            transaction.setType(TransactionType.CONTRACT);
            transaction.setFee(fee);
            transaction.setCreateTime(new Date());
            transactionService.save(transaction);
        } else {
            RobotTransaction robotTransaction = new RobotTransaction();
            if (order.getPositionFlag() == ContractEnumType.ContractOrderPositionFlag.throwout) {
                robotTransaction.setAmount(tradeAmount);
            } else {
                robotTransaction.setAmount(tradeAmount.negate());
            }
            robotTransaction.setSymbol(trade.getSymbol());
            robotTransaction.setMemberId(order.getMemberId());
            robotTransaction.setType(TransactionType.CONTRACT);
            robotTransaction.setFee(fee);
            robotTransaction.setCreateTime(new Date());
            robotTransactionService.save(robotTransaction);
        }
    }


    public List<ContractOrder> findByDirectionAndStatus(ContractOrderDirection sell, ContractOrderStatus nodeal) {
        return orderMapper.findByDirectionAndStatusOrderByPriceAsc(sell, nodeal);
    }

    public int pushWaitingOrderByOrderId(String orderId) {
        return orderMapper.pushWaitingOrderByOrderId(orderId);
    }


    /**
     * 订单交易完成
     *
     * @param orderId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public MessageResult orderCompleted(String orderId, BigDecimal start, BigDecimal tradedAmount, BigDecimal turnover, ContractOrder contractOrder) throws Exception {
        ContractOrder order = orderMapper.findByOrderId(orderId);
        if (order.getStatus() != ContractOrderStatus.TRADING && order.getStatus() != ContractOrderStatus.WAITING_TRIGGER) {
            return MessageResult.error(500, "invalid order(" + orderId + "),not trading status");
        }
        order.setTradedAmount(tradedAmount);
        order.setTurnover(turnover);
//        order.setStart(contractOrder.getStart());
        order.setStatus(ContractOrderStatus.COMPLETED);
        order.setCompletedTime(Calendar.getInstance().getTimeInMillis());
        order = orderMapper.saveAndFlush(order);
        //处理用户持有仓位信息
        //根据memberId锁表，防止死锁
        DB.query("select id from contract_wallet where member_id = ? for update;", order.getMemberId());
        DB.query("select id from contract_position where member_id = ? and contract_position_type = ? for update;", order.getMemberId() , order.getPositionType());
        boolean success = orderRefund(order, start, tradedAmount, turnover, ContractEnumType.CanceledOrCompleted.completed);
        if(success){
            return MessageResult.success("orderCompleted success");
        }else{
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return MessageResult.error("orderCompleted fall");
        }
    }

    public Page<ContractOrder> findAll(Predicate predicate, Pageable pageable) {
        return orderMapper.findAll(predicate, pageable);
    }

    public List<ContractOrder> findAllWaitingOrder(String symbol, ContractOrderStatus waitingTrigger) {
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        specification.add(Restrictions.eq("symbol", symbol, false));
        specification.add(Restrictions.eq("status", waitingTrigger, false));
        return orderMapper.findAll(specification);
    }

//    public boolean findTodayOrderCancelTimes(long id, String symbol) {
//        return false;
//    }

    /**
     * 获取某交易对当日已取消次数
     *
     * @param uid
     * @param symbol
     * @return
     */
    public long findTodayOrderCancelTimes(Long uid, String symbol) {
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        specification.add(Restrictions.eq("symbol", symbol, false));
        specification.add(Restrictions.eq("memberId", uid, false));
        specification.add(Restrictions.eq("status", ContractOrderStatus.CANCELED, false));
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        long startTick = calendar.getTimeInMillis();
        calendar.add(Calendar.HOUR_OF_DAY, 24);
        long endTick = calendar.getTimeInMillis();
        specification.add(Restrictions.gte("canceledTime", startTick, false));
        specification.add(Restrictions.lt("canceledTime", endTick, false));
        return orderMapper.count(specification);
    }


    /**
     * @param uid
     * @param pageNum
     * @param pageSize
     * @return
     */
    public Page<ContractOrder> findHistory(Long uid, String symbol, int pageNum, int pageSize) {
        Sort orders = new Sort(new Sort.Order(Sort.Direction.DESC, "listTime"));
        //从第一条开始查
        PageRequest pageRequest = new PageRequest(pageNum - 1, pageSize, orders);
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        specification.add(Restrictions.eq("symbol", symbol, true));
        specification.add(Restrictions.eq("memberId", uid, true));
        List<ContractOrderStatus> list = new ArrayList<>();
        list.add(ContractOrderStatus.CANCELED);
        list.add(ContractOrderStatus.COMPLETED);
        list.add(ContractOrderStatus.OVERTIMED);
        specification.add(Restrictions.in("status", list, false));
        return orderMapper.findAll(specification, pageRequest);
    }

    public Page<ContractOrder> findHistory(Long uid, String orderId, String status, int pageNum, int pageSize) {
        Sort sort = new Sort(new Sort.Order(Sort.Direction.DESC, "listTime"));
        PageRequest pageRequest = new PageRequest(pageNum - 1, pageSize, sort);
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        if (orderId != null || !orderId.equals("")) {
            specification.add(Restrictions.eq("symbol", orderId, true));
        }
        specification.add(Restrictions.eq("memberId", uid, false));
        //List<ContractOrderStatus> list = new ArrayList<>();
        //list.add(ContractOrderStatus.NODEAL);
        if (!status.equals("all")) {
            specification.add(Restrictions.eq("status", ContractOrderStatus.valueOf(status), true));
        }
        return orderMapper.findAll(specification, pageRequest);
    }

    public Page<ContractOrder> depth(String type, String symbol, int pageNum, int pageSize) {
        Sort sort = new Sort(new Sort.Order(Sort.Direction.DESC, "price"));
        PageRequest pageRequest = new PageRequest(pageNum - 1, pageSize, sort);
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        if (type.equals("buy")) {
            specification.add(Restrictions.eq("direction", ContractOrderDirection.BUY, true));
        } else {
            specification.add(Restrictions.eq("direction", ContractOrderDirection.SELL, true));
        }
        specification.add(Restrictions.eq("symbol", symbol, true));
        specification.add(Restrictions.eq("status", ContractOrderStatus.TRADING, true));
        return orderMapper.findAll(specification, pageRequest);
    }

    /**
     * 强制取消订单,在撮合中心和数据库订单不一致的情况下使用
     *
     * @param order
     */
    @Transactional
    public void forceCancelOrder(ContractOrder order) throws Exception {
        List<ContractOrderDetail> details = ContractOrderDetailService.findAllByOrderId(order.getOrderId());
        BigDecimal tradedAmount = BigDecimal.ZERO;
        BigDecimal turnover = BigDecimal.ZERO;
        BigDecimal start = BigDecimal.ZERO;
        for (ContractOrderDetail trade : details) {
            tradedAmount = tradedAmount.add(trade.getAmount());
            start = start.add(trade.getBond());
            turnover = turnover.add(trade.getAmount().multiply(trade.getPrice()));
        }
        order.setTradedAmount(tradedAmount);
        order.setTurnover(turnover);
        order.setStart(order.getStart().subtract(start));
        if (order.isCompleted()) {
            orderCompleted(order.getOrderId(), order.getStart(), order.getTradedAmount(), order.getTurnover(), order);
        } else {
            orderCanceled(order.getOrderId(), order.getStart(), order.getTradedAmount(), order.getTurnover());
        }
    }

    public List<ContractOrder> findOrders(ContractOrder order) {
        Example<ContractOrder> example = Example.of(order);
        return orderMapper.findAll(example);
    }

    public Page<ContractOrder> findPageByStatus(Long uid, String symbol, int pageNo, int pageSize, ContractOrderStatus status) {
        Sort orders = new Sort(new Sort.Order(Sort.Direction.DESC, "listTime"));
        PageRequest pageRequest = new PageRequest(pageNo - 1, pageSize, orders);
        Criteria<ContractOrder> specification = new Criteria();
        specification.add(Restrictions.eq("status", status, true));
        if (StringUtils.isNotBlank(symbol)) {
            specification.add(Restrictions.eq("symbol", symbol.toUpperCase(), true));
        }
        specification.add(Restrictions.eq("memberId", uid, true));
        return orderMapper.findAll(specification, pageRequest);
    }

    public Page<ContractOrder> findPageStop(Long uid, String symbol, int pageNo, int pageSize, ContractOrderStatus status, ContractOrderType type) {
        Sort orders = new Sort(new Sort.Order(Sort.Direction.DESC, "listTime"));
        PageRequest pageRequest = new PageRequest(pageNo - 1, pageSize, orders);
        Criteria<ContractOrder> specification = new Criteria();
        specification.add(Restrictions.eq("status", status, true));
        if (StringUtils.isNotBlank(symbol)) {
            specification.add(Restrictions.eq("symbol", symbol.toUpperCase(), true));
        }
        specification.add(Restrictions.eq("type", type, true));
        specification.add(Restrictions.eq("memberId", uid, true));
        return orderMapper.findAll(specification, pageRequest);
    }

    /**
     * 查询当前交易中的委托
     *
     * @param uid
     * @param pageNo
     * @param pageSize
     * @return
     */
    public Page<ContractOrder> findPageCurrent(Long uid, String symbol, int pageNo, int pageSize) {
        Sort orders = new Sort(new Sort.Order(Sort.Direction.DESC, "listTime"));
        PageRequest pageRequest = new PageRequest(pageNo - 1, pageSize, orders);
        Criteria<ContractOrder> specification = new Criteria();
        specification.add(Restrictions.eq("memberId", uid, true));
        if (StringUtils.isNotBlank(symbol)) {
            specification.add(Restrictions.eq("symbol", symbol.toUpperCase(), true));
        }
        List<ContractOrderStatus> list = new ArrayList<>();
        list.add(ContractOrderStatus.TRADING);
        specification.add(Restrictions.in("status", list, true));
        return orderMapper.findAll(specification, pageRequest);
    }

    /**
     * 查询当前交易中的委托
     *
     * @param uid
     * @return
     */
    public List<ContractOrder> findPageCurrent(Long uid, String symbol) {
        Criteria<ContractOrder> specification = new Criteria();
        specification.add(Restrictions.eq("memberId", uid, true));
        if (StringUtils.isNotBlank(symbol)) {
            specification.add(Restrictions.eq("symbol", symbol.toUpperCase(), true));
        }
        List<ContractOrderStatus> list = new ArrayList<>();
        list.add(ContractOrderStatus.TRADING);
        list.add(ContractOrderStatus.WAITING_TRIGGER);
        specification.add(Restrictions.in("status", list, true));
        return orderMapper.findAll(specification);
    }


    /**
     * 个人中心历史委托
     *
     * @param uid
     * @param symbol
     * @param type
     * @param status
     * @param startTime
     * @param endTime
     * @param pageNo
     * @param pageSize
     * @return
     */
    public Page<ContractOrder> findPersonalHistory(Long uid, String symbol, ContractOrderType type, ContractOrderStatus status, String startTime, String endTime, ContractOrderDirection direction, int pageNo, int pageSize, List<String> orderIds) {
        Sort orders = new Sort(new Sort.Order(Sort.Direction.DESC, "listTime"));
        PageRequest pageRequest = new PageRequest(pageNo - 1, pageSize, orders);
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        if (StringUtils.isNotEmpty(symbol)) {
            specification.add(Restrictions.eq("symbol", symbol, true));
        }
        if (type != null && StringUtils.isNotEmpty(type.toString())) {
            specification.add(Restrictions.eq("type", type, true));
        }
        if (direction != null && StringUtils.isNotEmpty(direction.toString())) {
            specification.add(Restrictions.eq("direction", direction, true));
        }
        if (orderIds != null && orderIds.size() > 0) {
            specification.add(Restrictions.in("orderId", orderIds, false));
        }
        specification.add(Restrictions.eq("memberId", uid, true));
//        if (StringUtils.isNotEmpty(startTime)&&StringUtils.isNotEmpty(endTime)) {
//            specification.add(Restrictions.gte("time", Long.valueOf(startTime), true));
//            specification.add(Restrictions.lte("time", Long.valueOf(endTime), true));
//        }

        if (status != null) {
//            List<ContractOrderStatus> list = new ArrayList<>();
//            list.add(ContractOrderStatus.CANCELED);
//            list.add(ContractOrderStatus.COMPLETED);
//            list.add(ContractOrderStatus.OVERTIMED);
//            specification.add(Restrictions.in("status", list, false));
            specification.add(Restrictions.eq("status", status, true));
        }
        return orderMapper.findAll(specification, pageRequest);
    }

    /**
     * 个人中心当前委托
     *
     * @param uid
     * @param symbol
     * @param type
     * @param startTime
     * @param endTime
     * @param pageNo
     * @param pageSize
     * @return
     */
    public Page<ContractOrder> findPersonalCurrent(Long uid, String symbol, ContractOrderType type, String startTime, String endTime, ContractOrderDirection direction, int pageNo, int pageSize) {
        Sort orders = new Sort(new Sort.Order(Sort.Direction.DESC, "time"));
        PageRequest pageRequest = new PageRequest(pageNo - 1, pageSize, orders);
        Criteria<ContractOrder> specification = new Criteria<ContractOrder>();
        if (StringUtils.isNotEmpty(symbol)) {
            specification.add(Restrictions.eq("symbol", symbol, true));
        }
        if (type != null && StringUtils.isNotEmpty(type.toString())) {
            specification.add(Restrictions.eq("type", type, true));
        }
        specification.add(Restrictions.eq("memberId", uid, false));
        if (StringUtils.isNotEmpty(startTime) && StringUtils.isNotEmpty(endTime)) {
            specification.add(Restrictions.gte("time", Long.valueOf(startTime), true));
            specification.add(Restrictions.lte("time", Long.valueOf(endTime), true));
        }
        if (direction != null && StringUtils.isNotEmpty(direction.toString())) {
            specification.add(Restrictions.eq("direction", direction, true));
        }
        List<ContractOrderStatus> list = new ArrayList<>();
        list.add(ContractOrderStatus.TRADING);
        list.add(ContractOrderStatus.WAITING_TRIGGER);
        specification.add(Restrictions.in("status", list, false));
        return orderMapper.findAll(specification, pageRequest);
    }


    /**
     * 强平操作
     *
     * @return
     * @throws Exception
     */
    @Transactional
    public void forceThrowout() {
        Map<Long, ContractPosition> map = ContractUtils.findAllPosition();

        map.keySet().stream().forEach(key -> {
            ContractPosition position = map.get(key);
            ContractCoin coin = ContractUtils.getBaseSymbol(position.getSymbol());
            BigDecimal marketPrice = ContractUtils.getMarketprice().get(coin.getSymbol());
            BigDecimal orderNum = new BigDecimal(position.getContractOccupyNum()).multiply(coin.getMinVolume());
            if (position.getHoldingBond().compareTo(BigDecimal.ZERO) > 0 && position.getContractTotalNum() > 0) {

                BigDecimal breakPrice = (orderNum.multiply(position.getPositionAvgPrice()).subtract(position.getHoldingBond())).divide(orderNum.multiply((new BigDecimal("1").subtract(new BigDecimal("0.00075")).setScale(8))), 8, BigDecimal.ROUND_DOWN);

                BigDecimal lowRate = (position.getHoldingBond().add(orderNum.multiply(marketPrice.subtract(position.getPositionAvgPrice())))).subtract(orderNum.multiply(new BigDecimal("0.00075")).multiply(breakPrice)).divide(orderNum.multiply(marketPrice), 8, BigDecimal.ROUND_DOWN);

                if (position.getPredictForcePrice().compareTo(marketPrice) >= 0) {
                    ContractOrder order = new ContractOrder();
                    if (position.getPositionType() == ContractOrderPositionType.RISE) {
                        order.setDirection(ContractOrderDirection.SELL);
                    }
                    if (position.getPositionType() == ContractOrderPositionType.FALL) {
                        order.setDirection(ContractOrderDirection.BUY);
                    }
                    order.setPositionFlag(ContractEnumType.ContractOrderPositionFlag.throwout);
                    order.setPositionOprationType(ContractEnumType.ContractOrderPositionOprationType.force);
                    order.setPrice(breakPrice);
                    order.setPositionType(position.getPositionType());
                    order.setNowp(new BigDecimal(position.getContractOccupyNum()));
                    order.setSymbol(position.getSymbol());
                    order.setStatus(ContractOrderStatus.NODEAL);
                    order.setBaseSymbol(coin.getBaseSymbol());
                    order.setCoinSymbol(coin.getCoinSymbol());
                    order.setListTime(Calendar.getInstance().getTimeInMillis());
                    order.setAmount(orderNum);
                    order.setMemberId(position.getMemberId());
                    order.setOrderResource(ContractOrderResource.API);
                    order.setType(ContractOrderType.LIMIT_PRICE);
                    order.setPositionValue(orderNum.multiply(coin.getMinVolume()).multiply(breakPrice));
                    order.setStopType(0);
                    order.setStart(position.getHoldingBond());
//                    JSONObject map = ContractUtils.getStart(orderNum,ContractUtils.getBaseSymbol(order.getSymbol()),order.getNowp(),order.getMemberId(),new BigDecimal(position.getCurrentMultiple()));
//                    JSONObject maintainMap = ContractUtils.getMaintain(orderNum,ContractUtils.getBaseSymbol(order.getSymbol()),order.getNowp(),order.getMemberId(),new BigDecimal(position.getCurrentMultiple()));
//                    BigDecimal start = map.getBigDecimal("start");
//                    String startRate = map.getString("startRate");
//                    BigDecimal maintain = maintainMap.getBigDecimal("maintain");
//                    String maintainRate = maintainMap.getString("maintainRate");
                    MessageResult mr = addOrder(order.getMemberId(), order);
                    positionService.saveAndFlush(position);
                    log.info("添加挂单成功=====================================================");
                    if (mr.getCode() == 0) {
                        //市价直接发送到 发送消息至Contract系统
                        kafkaTemplate.send("contract-order", order.getSymbol(), JSON.toJSONString(order));
                        log.info("结束le ================================");
                    }
                }
            }
        });
    }
}
